﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Configuration;
using System.Text.RegularExpressions;

namespace Crawler
{
    #region Catalog Class

    [Serializable]
    [XmlInclude(typeof(Word))]
    public class Catalog
    {
        #region Private Fields
        public static readonly Catalog Instance;
        private Hashtable index;
        #endregion

        #region Public Properties
        [XmlElement("words")]
        public Word[] Words
        {
            get
            {
                Word[] wordArray = new Word[index.Count];
                index.Values.CopyTo(wordArray, 0);
                return wordArray;
            }
            set
            {
                Word[] wordArray = value;
                index = new Hashtable();
            }
        }

        [XmlAttribute("dict")]
        public string[] Dictionary
        {
            get
            {
                string[] wordArray = new string[index.Count];
                index.Keys.CopyTo(wordArray, 0);
                return wordArray;
            }
        }

        public int Length
        {
            get { return index.Count; }
        }
        #endregion

        #region Constructor
        private Catalog()
        {
            index = new System.Collections.Hashtable();
        }

        static Catalog()
        {
            Instance = new Catalog();
        }
        #endregion

        #region Public Members
        public bool Add(string word, File file)
        {
            if (index.ContainsKey(word))
            {
                Word theword = (Word)index[word];
                theword.Add(file);
            }
            else
            {
                Word theword = new Word(word, file);
                index.Add(word, theword);
            }
            return true;
        }

        public File[] Search(string searchWord)
        {
            searchWord = Regex.Replace(searchWord, @"[^a-z0-9,.á|é|í|ó|ú|ü|ñ|Ñ]", "", RegexOptions.IgnoreCase);
            searchWord = searchWord.ToLower();
            searchWord = searchWord.Replace('á', 'a')
                    .Replace('é', 'e')
                    .Replace('í', 'i')
                    .Replace('ó', 'o')
                    .Replace('ú', 'u')
                    .Replace('ü', 'u')
                    .Replace('ñ', 'n');

            if (index.ContainsKey(searchWord))
            {
                return ((Word)index[searchWord]).Files;
            }

            return null;
        }

        internal File[] Search(string[] wordsArray)
        {
            File[] shortestFileArray = null;
            List<File[]> filesArrayList = new List<File[]>();

            foreach (string word in wordsArray)
            {
                File[] results = Catalog.Instance.Search(word);
                if (results!= null && results.Length > 0)
                {
                    if (shortestFileArray == null)
                    {
                        shortestFileArray = results;
                    }
                    else if (results.Length < shortestFileArray.Length)
                    {
                        shortestFileArray = results;
                    }
                    filesArrayList.Add(results);
                }
            }

            List<File> result = new List<File>();

            if (filesArrayList.Count > 1)
            {
                foreach (File pivoteFile in shortestFileArray)
                {
                    File fileResult = new File(pivoteFile);
                    long hits = fileResult.Hits;
 
                    foreach (File[] filesArray in filesArrayList)
                    {
                        if (filesArray == shortestFileArray) continue;

                        foreach (File file in filesArray)
                        {
                            if (file.Url == pivoteFile.Url)
                            {
                                hits *= file.Hits;
                            }
                        }
                    }

                    if (hits > fileResult.Hits)
                        result.Add(fileResult);
                }
            }
            else if (filesArrayList.Count == 1)
            {
                result = new List<File>(shortestFileArray);
            }
            result.Sort(CompareFilesByHist);
            return result.ToArray();
        }

        private static int CompareFilesByHist(File f1, File f2)
        {
            return (int)(f1.Hits - f2.Hits);
        }

        public void Save()
        {
            Stream stream = new FileStream(ConfigurationManager.AppSettings["Catalog"], FileMode.Create);
            IFormatter formatter = new BinaryFormatter();
            formatter.Serialize(stream, this);
            stream.Close();

            if (true)
            {
                XmlSerializer serializerXml = new XmlSerializer(typeof(Catalog));
                TextWriter writer = new StreamWriter("catalog.xml");
                serializerXml.Serialize(writer, this);
                writer.Close();

                XmlSerializer serializerXmlW = new XmlSerializer(typeof(string[]));
                TextWriter writerW = new StreamWriter("_words.xml");
                serializerXmlW.Serialize(writerW, this.Dictionary);
                writerW.Close();
            }
        }

        public static Catalog Load()
        {
            string catalogFile = ConfigurationManager.AppSettings["Catalog"];
            if (System.IO.File.Exists(catalogFile))
            {
                Stream stream = new FileStream(catalogFile, FileMode.Open);
                IFormatter formatter = new BinaryFormatter();
                object m = formatter.Deserialize(stream);
                stream.Close();
                Catalog catalog = m as Catalog;

                return catalog;
            }
            else
            {
                return null;
            }
        }
        #endregion

    }
    #endregion
}
